﻿<# 
.SYNOPSIS 
    Starts the PeopleSoft Application Server.
.DESCRIPTION 
    Use this command to start the PeopleSoft Application Server for a specific domain.
    The domain will boot in serial by default.
.NOTES 
    File Name  : Start-PsftDomain.ps1 
    Author     : Scott Kearney - scott@kearney.tk 
    Requires   : PowerShell V3 
.EXAMPLE 
    Start-PsftDomain -PS_HOME C:\psoft\PS_HOME -PS_CFG_HOME C:\psoft\PS_CFG_HOME -ServerType AppServer -Domain DOMAIN1
    Starts the domain named DOMAIN1.
.EXAMPLE 
    Start-PsftDomain C:\psoft\PS_HOME C:\psoft\PS_CFG_HOME DOMAIN1 -Parallel
    Starts the domain named DOMAIN1 in parallel boot more.
.PARAMETER PS_HOME
    The full path to the PS_HOME directory for this domain.
.PARAMETER PS_CFG_HOME
    The full path to the PS_CFG_HOME directory for this domain.
.PARAMETER ServerType
    The type of server on which to perform the action.
.PARAMETER Domain
    The name of the domain on which to perform the action.
.PARAMETER Parallel
    If specified, will start the domain in parallel boot mode.
#> 
function Start-PsftDomain
{
    [CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact='Medium')]
    Param
    (
        # The full path to the PS_HOME directory for this domain.
        # See http://docs.oracle.com/cd/E41633_01/pt853pbh1/eng/pt/tsvt/concept_UnderstandingPS_HOMEandPS_CFG_HOME-eb7ece.html
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage = "The full path to the PS_HOME directory for this domain.",
                   Position=0)]
        [ValidateScript({Validate_PS_HOME $_})]
        [String]
        $PS_HOME,

        # The full path to the PS_CFG_HOME directory for this domain.
        # See http://docs.oracle.com/cd/E41633_01/pt853pbh1/eng/pt/tsvt/concept_UnderstandingPS_HOMEandPS_CFG_HOME-eb7ece.html
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage = "The full path to the PS_CFG_HOME directory for this domain.",
                   Position=1)]
        [ValidateScript({Validate_PS_CFG_HOME $_})]
        [String]
        $PS_CFG_HOME,

        # The type of server on which to perform the action.
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage = "The type of server on which to perform the action.",
                   Position=2)]
        [ValidateSet("AppServer","PrcsScheduler","SearchServer","WebServer")]
        $ServerType,

        # The name of the domain on which to perform the action.
        # Should be a directory under the PS_CFG_HOME
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage = "The name of the domain on which to perform the action.",
                   Position=3)]
        [ValidateScript({Validate_Domain $PS_CFG_HOME $ServerType $_})]
        [String]
        $Domain,

        # If specified, will start the domain in parallel boot mode.
        # Only applies to AppServer domains. Otherwise is ignored.
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage = "If specified, will start the domain in parallel boot mode.",
                   Position=4)]
        [Switch]
        $Parallel
    )

    Begin
    {
    }
    Process
    {
        # Set up variables
        $return = @()
        $PSAdmin = @{Result=@();Error="";}
        $completedServers = 0
        $totalServers = 0
        
        switch ($ServerType)
        {
            'AppServer'     {
                                # Decide whether to boot in normal or parallel mode
                                if($Parallel){
                                    $Command = "parallelboot"
                                } else {
                                    $Command = "boot"
                                }

                                $UBBFile = "$PS_HOME\appserv\$Domain\psappsrv.ubb"
                            }
            'PrcsScheduler' {
                                $Command = "start"
                                $UBBFile = "$PS_HOME\appserv\prcs\$Domain\psprcsrv.ubb"
                            }
            'SearchServer'  {
                                $Command = "boot"
                                $UBBFile = "$PS_HOME\appserv\Search\$Domain\*.ubb"        # Need to update this once I know what this file is named.
                            }
            'WebServer'     {$Command = "boot"}
        }

        if($PSCmdlet.ShouldProcess($Domain, "Start-PsftDomain"))
        {
            # Retrieve the number of servers which will be started (used for progress bar)
           
            # Read the UBB file for all the server IDs, and one line below
            $UBBlines = gc $UBBFile | Select-String "^[\s]*SRVID" -Context 0,1
            
            # One server is counted for each server ID
            $totalServers = $UBBlines.Count + 1 
            
            # If the MIN is greater than one, add that count to the total
            $UBBlines.Context.PostContext | Select-String "MIN=" | %{ $totalServers += (($_ -split "=")[1] - 1)}


            # Invoke PSAdmin to boot the domain
            $PSAdminJob = Invoke-PSAdmin -PS_HOME $PS_HOME -PS_CFG_HOME $PS_CFG_HOME -ServerType $ServerType -Domain $Domain -Command $Command -AsJob

            # While data is being returned, parse it for the progress bar
            while($PSAdminJob | Select -exp HasMoreData){
                $ProgName = ""

                # Receive the new data
                $MoreData = ($PSAdminJob | Receive-Job) 

                # Parse the data based on type: String or non-String (usually Error stream)
                $thisErrors = (($MoreData | Where {$_.GetType() -notlike "String"}) -join "")
                $thisResults = $MoreData | Where {$_.GetType() -like "String"}
                
                # Add the data from this iteration of the loop to the running total
                $PSAdmin.Error += $thisErrors
                $PSAdmin.Result += $thisResults

                # Parse the data from this iteration, pulling out the server name
                $thisErrors -split "`n" | Select-String "^exec" | %{
                    $ProgName = (([String]$_.Line.TrimStart("exec ") -split " ")[0])
                    Write-Progress -Activity "Starting PeopleSoft $ServerType" -Status "$Domain" -CurrentOperation $ProgName -PercentComplete ((($completedServers++)/$totalServers)*100)
                }
            }

            # After the data is done processing, remove the progress bar
            Write-Progress -Activity "Starting PeopleSoft $ServerType" -Status "$Domain" -PercentComplete 100 -Completed

            # Now that we have all of the data, split it up into separate lines
            $PSAdmin.Error = $PSAdmin.Error -split "`n"

            # Write the information out, if verbose
            Write-Verbose "*** Errors ***"
            $PSAdmin.Error | %{ Write-Verbose $_ }
        
            Write-Verbose "*** Results ***"
            $PSAdmin.Result | %{ Write-Verbose $_ }

            # Catch any errors that came up during boot.
		    switch($PSAdmin.Result){
			    {$_ -match "The domain BBL is already running."} { Write-Error "The domain BBL is already running. If the domain is already running, parallel start can cause future problems." }
		    }
        
            # The $PSAdmin.Error portion has the information that we need to process
            $result = $PSAdmin.Error
		
            $return = @()

            # Parse out the resulting information and create an object with the relevant information.
            $result | Select-String "^exec" -Context 0,1 | ForEach-Object{
                $return += New-Object -TypeName Object |
                    Add-Member -MemberType NoteProperty -Name ProgName    -Value (([String]$_.Line.TrimStart("exec ") -split " ")[0]) -PassThru | 
                    Add-Member -MemberType NoteProperty -Name CommandLine -Value (([String]$_.Line -replace "exec [\w]*[\s]" -replace "[\s]:[\s].*")) -PassThru |
                    Add-Member -MemberType NoteProperty -Name ProcessID   -Value (    [int]($_.Context.PostContext -replace ".*process id=([\d]*).*","`$1" -replace "[^\d].*")[0]) -PassThru | 
                    Add-Member -MemberType NoteProperty -Name Status      -Value (([String]$_.Context.PostContext -replace ".*:[\s]*([^:]+)$","`$1" -replace "process id=[\d]*" -replace "\.\.\." -replace "^[\s]*") -replace "\..*$") -PassThru
            }
        }
        
        return $return
    }
    End
    {
    }
}